//==============================================================================
//
//               Copyright (C) 2005  Dick van Oudheusden
//  
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2007-10-30 21:17:43 $ $Revision: 1.2 $
//
//==============================================================================

#include <stdlib.h>
#include <math.h>

#include "ofc/DDoubleArray.h"

#include "DInc.h"
#include "DTest.h"


#define sizear(ar)   (sizeof(ar) / sizeof(ar[0]))

//-Datatype--------------------------------------------------------------------

void DDoubleArray_test(void)
{
  DDoubleArray *ar1    = [DDoubleArray alloc];
  DDoubleArray *ar2    = nil;
  DDoubleArray *ar3    = [DDoubleArray new  ];
  DText     *txt       = nil;
  char      *pntr;
  char       test[] = "  -4,  -8.3  ,34.534;1e12,";
  
  //                       0    1     2     3     4     5      6       7     8     9    10     11     12
  double     src1[] = { -2.8, 4.0, 4.01, -7.2, -7.3, 10.8, 10.81, 10.805, 4.01, -1.2, 4.01, -12.8, 10.79 };
  double     src2[] = { 1.0, 2.0, 3.0 }; // Insert slice
  
  double     tst1[] = { -2.8, 4.0, 4.01, -7.2, 1.0, 2.0, 3.0, 10.81, 10.805, 4.01, -1.2, 4.01, -12.8, 10.79 }; // Slice inserted
  double     tst2[] = { -2.8, 4.0, 4.01, -7.2, 1.0, 2.0, 3.0, 10.79 }; // Slice deleted
  double     tst3[] = { -2.8, 4.0, 4.01, -7.2, -7.3, 10.8, 10.81, 10.805, 4.01, -1.2, 4.01, -12.8, 1.0, 2.0, 3.0}; // Slice (at end) insert
  double     tst4[] = { 1.0, 2.0, 3.0, -7.3, 10.8, 10.81, 10.805, 4.01, -1.2, 4.01, -12.8, 1.0, 2.0, 3.0}; // Slice delete and insert (at front) 
  double     tst5[] = { 2.0, 3.0, -7.3 }; // Sub array
  double     tst6[] = { -2.8, 4.0, 4.01, 1.0, 2.0, 3.0, -7.2, -7.3, 10.8, 10.81, 10.805, 4.01, -1.2, 4.01, -12.8, 10.79 };
  double     tst7[] = { 1.0, 2.0, 3.0, -2.8, 4.0, 4.01, -7.2, -7.3, 10.8, 10.81, 10.805, 4.01, -1.2, 4.01, -12.8, 10.79, 3.0, 2.0, 1.0 }; // Append and inverted prepend
  double     tst8[] = { -2.8, 4.0, 4.01, -7.2, -12.8, 4.01, -1.2, 4.01, 10.805, 10.81, 10.8, -7.3, 10.79 };
  double     tst9[] = { -4.0, -8.3, 34.534, 1e12 }; // from string
  double     tsta[] = { -12.8, -7.3, -7.2, -2.8, -1.2, 4.0, 4.01, 4.01, 4.01, 10.79, 10.8, 10.805, 10.81 }; // Sorted
  double     tstb[] = { -2.8, 4.0, 4.01, -7.2, -7.3, -1.2, 4.01, 10.8, 10.805, 10.81, 4.01, -12.8, 10.79 }; // 4-9 sorted
  
  STARTTEST();

  // Init with array
  TEST([ar1 init :src1 :sizear(src1)] != nil);
  
  TEST([ar1 length] == sizear(src1));
  TEST(![ar1 isEmpty]);
  
  // Init by copy
  ar2 = [ar1 copy]; 
  
  TEST([ar2 length] == sizear(src1));
  
  TEST([ar1 compare :ar2] == 0);

  TEST([ar1 array] != [ar2 array]);

  // Default init
  TEST([ar3 length] == 0);
  TEST([ar3 isEmpty]);
  
  // Clear the array
  TEST([[ar1 clear] length] == 0);
  TEST([ar1 isEmpty]);
  
  // Set the object with an array
  TEST([[ar1 set :src1 :sizear(src1)] length] == sizear(src1));
  
  // Elements in array
  TEST( [ar1 get :-1] == 10.79);
  TEST([[ar1 put :12 :2.0] get :-1] == 2.0);
  TEST([[ar1 put :12 :10.79] length] == sizear(src1));

  TEST([ar1 bcompare :src1 :sizear(src1)] == 0);
  
  // Insert & delete elements
  TEST([[ar1 insert :0 :3.15] length] == (sizear(src1)+1));
  TEST([ar1 get :0] == 3.15); 
  
  TEST([[ar1 insert :-1 :2.0] length] == (sizear(src1)+2));
  TEST([ar1 get :-2] == 2.0);
  TEST([ar1 get :-1] == 10.79);
  
  TEST([[ar1 insert :7 :5.7] length] == (sizear(src1)+3));
  TEST([ar1 get :7] == 5.7); 
  
  [ar1 delete :7];
  [ar1 delete :-2];
  [ar1 delete :0];
  
  TEST([ar1 compare :ar2] == 0);
  
  // Insert & delete slices
  [ar1 insert :4 :5 :src2 :sizear(src2)];
  
  TEST([ar1 bcompare :tst1 :sizear(tst1)] == 0);
  
  [ar1 delete :7 :12];
  
  TEST([ar1 bcompare :tst2 :sizear(tst2)] == 0);
  
  [ar1 set :src1 :sizear(src1)];

  [ar1 insert :-1 :-1 :src2 :sizear(src2)];
  
  TEST([ar1 bcompare :tst3 :sizear(tst3)] == 0);  
  
  [ar1 delete :0 :2];

  [ar1 insert :0 :0 :src2 :sizear(src2)];
                                    
  TEST([ar1 bcompare :tst4 :sizear(tst4)] == 0); 
                                                         
  [ar2 free];
  
  ar2 = [ar1 get :1 :3];
  TEST([ar2 length] == 3);
  
  TEST([ar2 bcompare :tst5 :sizear(tst5)] == 0); 
  TEST([ar1 bcompare :tst4 :sizear(tst4)] == 0);

  [ar1 set :src1 :sizear(src1)];
  
  [ar1 insert :3 :2 :src2 :sizear(src2)]; // true sliced insert

  TEST([ar1 bcompare :tst6 :sizear(tst6)] == 0);
  
  // Append & prepend
  [ar1 set :src1 :sizear(src1)]; 
  [ar2 set :src2 :sizear(src2)];
  
  [ar2 invert :0 :-1];
  
  [ar1 prepend :src2 :sizear(src2)]; 
  [ar1 append  :[ar2 array] :[ar2 length]];
  
  TEST([ar1 bcompare :tst7 :sizear(tst7)] == 0); 
  
  // LIFO & FIFO
  [ar1 clear];
  
  [ar1 push :2.0];
  [ar1 push :3.0];
  TEST([ar1 tos] == 3.0);
  [ar1 push :-7.3];
  
  TEST([ar1 length] == 3);
  TEST([ar1 bcompare :tst5 :sizear(tst5)] == 0);
  
  TEST([ar1 pop] == -7.3);
  TEST([ar1 pop] == 3.0);
  TEST([ar1 tos] == 2.0);
  TEST([ar1 pop] == 2.0);
  TEST([ar1 length] == 0);
  TEST([ar1 pop] == 0);
  
  [ar1 clear];

  [ar1 enqueue :3.0];
  [ar1 enqueue :2.0];
  [ar1 enqueue :1.0];
  
  TEST([ar1 length] == 3);
  TEST([ar1 bcompare :src2 :sizear(src2)] == 0);
  
  TEST([ar1 dequeue] == 3.0);
  TEST([ar1 dequeue] == 2.0);
  TEST([ar1 dequeue] == 1.0);
  
  TEST([ar1 length] == 0);
  
  TEST([ar1 dequeue] == 0);
  
  // Statistical methods
  [ar1 set :src1 :sizear(src1)];

  TEST([ar1 count :4.01 :0 :-1] == 3);
  TEST([ar1 count :4.01 :0 :7 ] == 1);
  
  TEST([ar1 index :4.0  :0   :-1] == 1);
  TEST([ar1 index :4.01 :-10 :-1] == 8);
  TEST([ar1 index :-1.2 :0   :8 ] == -1);
  TEST([ar1 index :4.02 :0   :-1] == -1);
  
  TEST([ar1 rindex :4.01 :0 :-1] == 10);
  TEST([ar1 rindex :4.01 :0 :7 ] == 2);
  TEST([ar1 rindex :4.02 :7 :0 ]  == -1);
  
  TEST(fabs([ar1 sum :0 :-1] - 27.935) < 0.001);
  TEST(fabs([ar1 sum :4 :9] - 27.925) < 0.001);
  TEST(fabs([ar1 max :0 :-1] - 10.81) < 0.001);
  TEST(fabs([ar1 max :4 :9] - 10.81)  < 0.001);
  TEST(fabs([ar1 min :0 :-1] - (-12.8)) < 0.001);
  TEST(fabs([ar1 min :4 :9] - (-7.3)) < 0.001);
  TEST(fabs([ar1 average :0 :-1] - 2.148) < 0.001);
  TEST(fabs([ar1 average :4 :9] - 4.654) < 0.001);
  TEST(fabs([ar1 variance :0 :-1] - 57.625) < 0.001); 
  TEST(fabs([ar1 variance :4 :9] - 48.514) < 0.001);
  TEST(fabs([ar1 standardDeviation :0 :-1] - 7.591) < 0.001);
  TEST(fabs([ar1 standardDeviation :4 :9] - 6.965) < 0.001);

  // Invert the array
  [ar1 set :src1 :sizear(src1)];
  
  [ar1 invert :4 :11];
  
  TEST([ar1 bcompare :tst8 :sizear(tst8)] == 0);
  
  // text methods
  [ar1 set :src1 :sizear(src1)];
  
  txt = [ar1 toText]; 
  
  TEST([txt ccompare :"-2.8,4,4.01,-7.2,-7.3,10.8,10.81,10.805,4.01,-1.2,4.01,-12.8,10.79"] == 0);

  pntr = (char *) [txt cstring];
  
  TEST([ar2 fromString :&pntr] == 0);
  TEST([ar1 compare :ar2] == 0);
  TEST(*pntr == EOS);
  
  pntr = test;
  TEST([ar2 fromString :&pntr] == 0); 
  TEST([ar2 bcompare :tst9 :sizear(tst9)] == 0);
  TEST(*pntr == ',');
  
  [txt free];

  // sorting
  [ar1 set :src1 :sizear(src1)];
  [ar1 sort :0 :-1];
  TEST([ar1 bcompare :tsta :sizear(tsta)] == 0);
  
  [ar1 set :src1 :sizear(src1)];
  [ar1 sort :4 :9];
  TEST([ar1 bcompare :tstb :sizear(tstb)] == 0);

  [ar3 free];
  [ar2 free];
  [ar1 free];
  
  STOPTEST();
}

